package com.trippin.DB;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Set;

import com.trippin.DB.entities.AttractionEntity;
import com.trippin.DB.entities.AttractionEntity.ShortDetailedAttraction;
import com.trippin.DB.entities.Comment;
import com.trippin.DB.entities.ScheduledAttractionEntity;
import com.trippin.DB.entities.SharingRequest;
import com.trippin.DB.entities.StickyNoteEntity;
import com.trippin.DB.entities.TripEntity;
import com.trippin.DB.entities.TripEntity.ShortDetailedTrip;
import com.trippin.DB.entities.UserEntity;
import com.trippin.DB.entities.UserNotification;
import com.trippin.DB.entities.UserSuggestion;

public abstract class DBHandler {

	
	public static DBHandler INSTANCE = DataStoreHandler.getInstance();


	/**
	 * @param userId - the user ID.
	 * @param latN  - one of the coordinate of the google map square shown in client.
	 * @param lonE - one of the coordinate of the google map square shown in client.
	 * @param latS - one of the coordinate of the google map square shown in client.
	 * @param lonW - one of the coordinate of the google map square shown in client.
	 * @param startTime - the dateTime to start from.
	 * @param endTime - the dateTime to end in.
	 * @return all the attractions basic info of that location:
	 *  	if ((start != null) && (end  != null)) return all attractions between start and end.
	 *  	else if (start != null) return all attractions from start and on.
	 *  	else return all attractions.
	 */
	public abstract Iterable<ShortDetailedAttraction> getAttractions(long userId, double latN, double lonE, double latS, double lonW, 
			Date startTime, Date endTime);


	/**
	 * 
	 * @param attrId - the attraction ID.
	 * @return the attraction full details
	 */
	public abstract AttractionEntity getAttractionById(long userId, long attrId);

	/**
	 * 
	 * @param atrName - the attraction name.
	 * @return all the attractions that their name contains 'atrName'
	 */
	public abstract Iterable<AttractionEntity> getAttractionsContains(String atrName);

	/**
	 * @param userId  - the user ID.
	 * @param attrId - the attraction ID.
	 * @param start - the dateTime to start from.
	 * @param end - the dateTime to end in.
	 * @return all the schedule attractions basic info of that attraction:
	 *  	if ((start != null) && (end  != null)) return all schedule attractions between start and end.
	 *  	else if (start != null) return all schedule attractions from start and on.
	 *  	else return all schedule attractions.
	 */
	public abstract Iterable<ScheduledAttractionEntity> getScheduleOfAttraction(long userId, long attrId, Date start,
			Date end);

	/**
	 * create a new attraction according to the given AttractionEntity.
	 * @param userId - the user ID.
	 * @param attraction - the attraction Entity to be saved.
	 * @return the created attraction.
	 */
	public abstract AttractionEntity createNewAttraction(long userId, AttractionEntity attraction);

	/**
	 * update an existing attraction (iff attraction.getUserId() == userId)
	 * @param attId - the attraction ID.
	 * @param userId - the user ID.
	 * @param attraction - the attraction Entity to be saved.
	 * @return the updated attraction.
	 */
	public abstract AttractionEntity updateAttraction(long userId, long attId, AttractionEntity attraction);
		
	/**
	 * deletes the attraction by its ID (iff attraction.getUserId() == userId && attraction.getAttendees.isEmpty())
	 * @param atrId - the attraction ID.
	 * @param userId - the user ID.
	 * @throws IOException if the user does not have delete privileges for this attraction.
	 */
	public abstract void deleteAttraction(long userId, long atrId) throws IOException;

	
	/**
	 * register the user to the given attraction (by its ID).
	 * the user will be register to the attraction without a specific schedule.
	 * @param userId - the user ID
	 * @param attId - the attraction ID.
	 * @param tripId - the user's trip ID.
	 * @return the attraction
	 * @throws IOException
	 */
	public abstract AttractionEntity attendToAttraction(long userId, long tripId, long attId) throws IOException;
	
	/**
	 * 
	 * @param userId - the user ID
	 * @param attId - the attraction ID.
	 * @param tripId - the user's trip ID.
	 * @return the attraction
	 */
	public abstract AttractionEntity unAttendFromAttraction(long userId, long tripId,
			long attId);
	
	
	/**
	 * 
	 * @param userId - the user ID
	 * @param attId - the attraction ID.
	 * @param addPlus - true for adding a plus, false for adding a minus. 
	 * @return if(addPlus) the attraction where before.getPluses() + 1 == current.getPluses() || before.getMinuses() - 1 == current.getMinuses()
	 * 			else the attraction where before.getMinuses() + 1 == current.getMinuses() || before.getPluses() - 1 == current.getPluses()
	 */
	public abstract AttractionEntity addPlusOrMinusToAttraction(long userId, long attrId, boolean addPlus);
	/**
	 * 
	 * @param userId - the user ID
	 * @param attId - the attraction ID.
	 * @param comment
	 * @return the attraction.
	 */
	public abstract AttractionEntity addCommentToAttraction(long userId, long attId, Comment comment);

	/**
	 * 
	 * @param userId - the user ID
	 * @param attId - the attraction ID.
	 * @param commentId = the comment ID.
	 */
	public abstract void removeCommentFromAttraction(long userId, long attrId, long commentId);
	
	/**
	 * add the user to the schedule attraction's attendees.
	 * @param userId - the user ID.
	 * @param tripId - the user's trip ID.
	 * @param attrId - the attraction ID.
	 * @param scheduleAtrId - the schedule attraction ID.
	 * @return the attended attraction entity.
	 * @throws IOException
	 */
	public abstract AttractionEntity attendToScheduleAttraction (
			long userId, long tripId, long attrId, long scheduleAtrId)  throws IOException;

	/**
	 * remove the user from the schedule attraction's attendees.
	 * @param scheduleAtrId - the schedule attraction ID.
	 * @param userId - the user ID.
	 * @return the attended attraction entity.
	 */
	public abstract AttractionEntity unAttendFromScheduleAttraction(
			long userId, long tripId, long attrId, long scheduleAtrId);

	/**
	 * create a schedule attraction and attend the user to it.
	 * @param userId - the user ID.
	 * @param attrId - the related attraction ID.
	 * @param startTime - the start DateTime of the schedule attraction.
	 * @param endTime - the end DateTime of the schedule attraction.
	 * @return the created attraction entity.
	 * @throws IOException
	 */
	public abstract AttractionEntity createNewScheduleAttraction (
			long userId, long tripId, long attrId, Date date)  throws IOException;

	
	/**
	 * 
	 * @param userId - the user ID
	 * @param scheduleAttrId - the schedule attraction ID.
	 * @param addPlus - true for adding a plus, false for adding a minus. 
	 * @return if(addPlus) the schedule attraction where before.getPluses() + 1 == current.getPluses() || before.getMinuses() - 1 == current.getMinuses()
	 * 			else the attraction where before.getMinuses() + 1 == current.getMinuses() || before.getPluses() - 1 == current.getPluses()
	 */
	public abstract ScheduledAttractionEntity addPlusOrMinusToScheduleAttraction(long userId, long scheduleAttrId, boolean addPlus);
	
	
	/**
	 * 
	 * @param userId - the user ID.
	 * @param tripId - the Trip ID.
	 * @return the user's Trip.
	 */
	public abstract TripEntity getUserTrip(long userId, long tripId);

	/**
	 * 
	 * @param userId - the user ID.
	 * @return all the user's trips (short detailed)
	 */
	public abstract Iterable<ShortDetailedTrip> getAllUserTrips(long userId);
	
	/**
	 * 
	 * @param userId - the user ID.
	 * @param trip - the new trip to be created
	 * @return the created trip
	 * @throws IOException if the given days or dates are invalid
	 */
	public abstract TripEntity createTrip(long userId, TripEntity trip)  throws IOException;
		
	/**
	 * 
	 * @param userId - the user ID.
	 * @param tripId - the Trip ID.
	 * @param trip - the new trip to be update to
	 * @return the updated trip
	 * @throws IOException if the given days or dates are invalid
	 */
	public abstract TripEntity updateTrip(long userId, long tripId, TripEntity trip)  throws IOException;
	
	
	/**
	 * delete the trip while attend all of the user's attraction/schedule attraction attended by the user in this trip
	 * @param userId - the user ID.
	 * @param tripId - the Trip ID to be deleted.
	 */
	public abstract void deleteTrip(long userId, long tripId);
	
	
	/**
	 * 
	 * @param userId - the user ID.
	 * @return the user profile
	 */
	public abstract UserEntity getUserById(long userId);

	/**
	 * 
	 * @param userId - the user ID.
	 * @param user data
	 * @return created user profile
	 * @throws IOException 
	 */
	public abstract UserEntity createUser(long userId, UserEntity user) throws IOException;

	/**
	 * 
	 * @param userId - the user ID.
	 * @param user data
	 * @return the updated user profile
	 * @throws IOException 
	 */
	public abstract UserEntity updateUser(long userId, UserEntity user) throws IOException;

	/**
	 * add a trip sharing request to the user (shareWith)
	 * @param userId - the user ID.
	 * @param shareTripId - the tripId to be shared.
	 * @param shareWith - the userId the share the trip with.
	 * @throws IOException 
	 */
	public abstract void createTripSharingRequest(long userId, long shareTripId, long shareWith) throws IOException;


	/**
	 * 
	 * @param userId - the user ID.
	 * @return all the user's pending trip sharing invitations
	 * @throws IOException 
	 */
	public abstract UserNotification<SharingRequest> getUserTripSharingRequests(long userId) throws IOException;


	/**
	 * add the user to the trip and remove the notification
	 * @param userId - the user ID.
	 * @param sharingReqId - the requset ID
	 * @return the remaining of the user's trip sharing requests
	 * @throws IOException 
	 */
	public abstract UserNotification<SharingRequest> acceptTripSharingRequset(long userId, long sharingReqId) throws IOException;

	/**
	 * remove the notification
	 * @param userId - the user ID.
	 * @param sharingReqId - the requset ID
	 * @return the remaining of the user's trip sharing requests
	 */
	public abstract UserNotification<SharingRequest> deniedTripSharingRequset(long userId, long sharingReqId) throws IOException;

	
	/**
	 * 
	 * @param userId - the user ID.
	 * @param tripId - the user's trip ID.
	 * @return users that travel near me in the same time
	 */
	public abstract UserNotification<UserSuggestion> getUsersNearMe (
			long userId, long tripId);

	/**
	 * 
	 * @param userId - the user ID.
	 * @param tripId - the user's trip ID.
	 * @return attractions the user may like, that is near his current trip
	 */
	public abstract UserNotification<ShortDetailedAttraction> getAttractionsUserMayLike(long userId,
			long tripId);

	/**
	 * 
	 * @param userId - the user ID.
	 * @param latN  - one of the coordinate of the google map square shown in client.
	 * @param lonE - one of the coordinate of the google map square shown in client.
	 * @param latS - one of the coordinate of the google map square shown in client.
	 * @param lonW - one of the coordinate of the google map square shown in client.
	 * @param startTime - the dateTime to start from.
	 * @param endTime - the dateTime to end in.
	 * @return all sticky notes that exists in that bound and time frame.
	 */
	public abstract Set<StickyNoteEntity> getStickyNotesByBound(long userId, double latN,
			double lonE, double latS, double lonW, Date startTime, Date endTime);


	/**
	 * 
	 * @param userId - the user ID.
	 * @param sticktNote - the Sticky Note to be created (persisted),
	 * @return the created sticky note
	 * @throws IOException
	 */
	public abstract StickyNoteEntity createStickyNote(long userId,
			StickyNoteEntity sticktNote) throws IOException;

	/**
	 * 
	 * @param userId - the user ID.
	 * @param stickyNoteId - the sticky note ID.
	 * @return all sticky notes (of other users) in the bounds and dates of the user's sticky note
	 * @throws IOException 
	 */
	public abstract Set<StickyNoteEntity> getAllStickyNoteOfUserStickyNote(long userId,
			long stickyNoteId) throws IOException;

	/**
	 * 
	 * @param userId - the user ID.
	 * @return a list of the user's sticky notes
	 */
	public abstract List<StickyNoteEntity> getAllUserStickyNotes(long userId);

	/**
	 * delete the user's sticky note
	 * @param userId - the user ID.
	 * @param stickyNoteId - the sticky note ID.
	 * @return the remaining sticky notes of the user 
	 * @throws IOException 
	 */
	public abstract Set<StickyNoteEntity> deleteStickyNote(long userId, long stickyNoteId) throws IOException;


}
